using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Data;

using NBear.Common;
using NBear.Data;

namespace NBear.Test.UnitTests
{
    [Table("Categories")]
    public interface Category : IEntity
    {
        [PrimaryKey]
        int CategoryID { get; }
        string CategoryName { get; set; }
        string Description { get; set; }
        System.Byte[] Picture { get; set; }
    }

    [Table("Order Details")]
    public interface OrderDetails : IEntity
    {
        [PrimaryKey]
        int OrderID { get; set; }
        [PrimaryKey]
        int ProductID { get; set; }
        decimal UnitPrice { get; set; }
        short Quantity { get; set; }
        float Discount { get; set; }
    }

    [Table("Orders")]
    public interface Order : IEntity
    {
        [PrimaryKey]
        int OrderID { get; }
        string CustomerID { get; set; }
        int EmployeeID { get; set; }
        System.DateTime OrderDate { get; set; }
        System.DateTime RequiredDate { get; set; }
        System.DateTime ShippedDate { get; set; }
        int ShipVia { get; set; }
        decimal Freight { get; set; }
        string ShipName { get; set; }
        string ShipAddress { get; set; }
        string ShipCity { get; set; }
        string ShipRegion { get; set; }
        string ShipCountry { get; set; }
        string ShipPostalCode { get; set; }
    }

    [Table("Orders", AdditionalInsert = "[ShipCountry] = 'China', [ShipPostalCode] = '200000'", AdditionalWhere = "[ShipCountry] = 'China' and [ShipPostalCode] = '200000'")]
    public interface Order2 : IEntity
    {
        [PrimaryKey]
        int OrderID { get; }
        string CustomerID { get; set; }
        int EmployeeID { get; set; }
        System.DateTime OrderDate { get; set; }
        System.DateTime RequiredDate { get; set; }
        System.DateTime ShippedDate { get; set; }
        int ShipVia { get; set; }
        decimal Freight { get; set; }
        string ShipName { get; set; }
        string ShipAddress { get; set; }
        string ShipCity { get; set; }
        string ShipRegion { get; set; }
    }

    [Table("Order Details Extended", IsView = true)]
    public interface OrderDetailsExtended : IEntity
    {
        int OrderID { get; }
        int ProductID { get; }
        string ProductName { get; }
        [ColumnName("ProductName")]
        string ProductName2 { get; }
        decimal UnitPrice { get; }
        short Quantity { get; }
        float Discount { get; }
        decimal ExtendedPrice { get; }
    }

    public interface OrderDetailsExtended2 : IEntity
    {
        int OrderID { get; }
        int ProductID { get; }
        string ProductName { get; }
        string ProductName2 { get; }
        decimal UnitPrice { get; }
        short Quantity { get; }
        float Discount { get; }
        decimal ExtendedPrice { get; }

        SampleCustomPropertyType2 Sample2 { get; }
    }

    public class SampleCustomPropertyType2 : CustomPropertyType
    {
        public string Value;

        public SampleCustomPropertyType2(KeyValueCollection keyValues, string[] parameters)
            : base(keyValues, parameters)
        {
            Value = parameters[0];
        }
    }

    [Table("select od.OrderID, od.Discount from [Order Details] od inner join Products on Products.ProductID = od.ProductID", IsView = true)]
    public interface OrderProduct : IEntity
    {
        [PrimaryKey]
        int OrderID { get; }
        float Discount { get; set; }
    }

    /// <summary>
    /// Summary description for TestGateway
    /// </summary>
    [TestClass]
    public class TestGateway
    {
        private static Gateway gateway;

        public TestGateway()
        {
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        [ClassInitialize()]
        public static void MyClassInitialize(TestContext testContext) 
        {
            BaseEntityFactory.PreGenerateAllEntities();

            gateway = new Gateway(DatabaseType.SqlServer, "Server=(local);Database=Northwind;Uid=sa;Pwd=sa");
            Gateway.SetSqlLogger(new LogHandler(Console.Write));
        }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        [TestMethod]
        public void TestStrongTypeQuerys()
        {
            gateway.Delete<Category>(0);
            gateway.Delete<Category>("CategoryID = @ID", new object[] { 0 });
            gateway.Exists<Order>(1);
            gateway.Exists<Category>("CategoryID = @ID", new object[] { 1 });
            Order order = gateway.Get<Order>(10248);
            Order2 order2 = EntityFactory<Order>.ConvertToEntity<Order2>(order);
            Category obj = gateway.Get<Category>("CategoryID = @ID", new object[] { 1 });
            using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required))
            {
                gateway.Insert<Category>(obj);
                gateway.Update<Category>(obj);
                obj.GetKeyValues()[0] = 0;
                gateway.Insert<Category>(obj);
                scope.Complete();
            }
            gateway.SelectAll<Category>();
            gateway.Select<Category>(null, "CategoryID desc", null);
            gateway.Select<Category>(3, 3, null, "CategoryID desc", null);
            OrderDetailsExtended[] orders = gateway.SelectAll<OrderDetailsExtended>("OrderID");
            gateway.Update<Order>(order, "CustomerID");
            gateway.Update<Order2>(order2, "CustomerID");
            gateway.Update<Order>(new string[] { "CustomerID" }, new object[] { order.CustomerID }, order.OrderID);
            gateway.Update<Order2>(new string[] { "CustomerID" }, new object[] { order2.CustomerID }, order2.OrderID);
            order2.GetKeyValues()[0] = 0;
            gateway.Insert<Order2>(order2);

            Assert.IsTrue(gateway.SelectRowCount<Category>(null, null) > 0);
            Assert.IsTrue(gateway.SelectRowCount<OrderProduct>(null, null) > 0);
        }

        [TestMethod]
        public void TestCreateObjectListByColumnName()
        {
            System.Data.DataSet ds = gateway.SelectDataSet("select top 10 * from Categories", null);
            System.Data.IDataReader reader = gateway.SelectDataReader("select top 10 * from Categories", null);

            Category[] cats = EntityFactory<Category>.CreateObjectList(ds.Tables[0]);
            Category[] cats2 = EntityFactory<Category>.CreateObjectList(reader);
        }

        //[TestMethod]
        public void TestSimplePerformance()
        {
            long timeCount = DateTime.Now.Ticks;
            for (int i = 0; i < 100; i++)
            {
                System.Data.DataSet ds = gateway.SelectDataSet("select * from Categories", null);
            }
            timeCount = DateTime.Now.Ticks;
            Console.WriteLine("entity: " + timeCount.ToString());

            timeCount = DateTime.Now.Ticks;
            for (int i = 0; i < 100; i++)
            {
                System.Data.IDataReader reader = gateway.SelectDataReader("select * from Categories", null);
                Category[] cats2 = EntityFactory<Category>.CreateObjectList(reader);
            }
            timeCount = DateTime.Now.Ticks;
            Console.WriteLine("dataset: " + timeCount.ToString());
        }

        [TestMethod]
        public void TestJoinQuery()
        {
            OrderProduct[] objs = gateway.Select<OrderProduct>("OrderID > 0", "OrderID desc", null);
            objs = gateway.Select<OrderProduct>(2, 1, "OrderID > 0", "OrderID desc", null);
            objs = gateway.Select<OrderProduct>(2, 2, "OrderID > 0", "OrderID desc", null);
        }

        //[TestMethod]
        public void TestOutputParameter()
        {
            object[] result;
            gateway.ExecuteProcedureReadOnly("SalesByCategoryTest", new string[] { "@CategoryName", "@OrdYear" }, new object[] { "teddy", "1997" }, new string[] { "@OutP" }, new DbType[] { DbType.String }, out result);
            Assert.AreEqual(result[0], "teddy");
        }

        [TestMethod]
        public void TestEntityConfiguration()
        {
            Assert.AreEqual(Entity<OrderDetailsExtended2>.GetTableName(), "Order Details Extended");
            Assert.IsTrue(Entity<OrderDetailsExtended2>.IsView());
            Assert.IsTrue(Entity<OrderDetailsExtended2>.GetPrimaryKeyColumnNames().Length == 2);
            Assert.AreEqual(Entity<OrderDetailsExtended2>.GetPropertyMappedColumn("ProductName2"), "ProductName");
            Assert.AreEqual(EntityFactory<OrderDetailsExtended2>.CreateObject().Sample2.Value, "Sample2");
        }

        [TestMethod]
        public void TestStrongTypedExecuteProcedure()
        {
            SalesByYear[] objs = gateway.ExecuteProcedure<SalesByYear>(_Entity.SalesByYear.Beginning_Date == DateTime.Parse("1982-1-1") & _Entity.SalesByYear.Ending_Date == DateTime.Parse("2000-1-1"));
            SalesByYear2[] objs2 = gateway.ExecuteProcedure<SalesByYear2>(_Entity.SalesByYear.Beginning_Date == DateTime.Parse("1982-1-1") & _Entity.SalesByYear.Ending_Date == DateTime.Parse("2000-1-1"));
        }

        [TestMethod]
        public void TestStoredProcedureEntityConfiguration()
        {
            Assert.AreEqual(Entity<OrderDetailsExtended2>.GetStoreProcedureName(), "TeddyProcedure");
            Assert.AreEqual(Entity<OrderDetailsExtended2>.GetStoreProcedureFillByIndex(), true);
            Assert.AreEqual(Entity<SalesByYear>.GetStoreProcedureName(), "Sales by Year");
            Assert.AreEqual(Entity<SalesByYear>.GetStoreProcedureFillByIndex(), false);
            Assert.AreEqual(Entity<OrderDetails>.GetStoreProcedureName(), "OrderDetails");
            Assert.AreEqual(Entity<OrderDetails>.GetStoreProcedureFillByIndex(), false);
        }
    }

    [StoreProcedure("Sales by Year")]
    public interface SalesByYear : IEntity
    {
        DateTime ShippedDate { get; set; }
        int OrderID { get; set; }
        decimal Subtotal { get; set; }
        string Year { get; set; }
    }

    [StoreProcedure("Sales by Year", FillByIndex=true)]
    public interface SalesByYear2 : IEntity
    {
        DateTime ShippedDate { get; set; }
        int OrderID { get; set; }
        decimal Subtotal { get; set; }
        string Year { get; set; }
    }

    namespace _Entity
    {
        public class SalesByYear
        {
            public static PropertyItem ShippedDate = new PropertyItem("ShippedDate");
            public static PropertyItem OrderID = new PropertyItem("OrderID");
            public static PropertyItem Subtotal = new PropertyItem("Subtotal");
            public static PropertyItem Year = new PropertyItem("Year");
            public static ProcedureParamItem Beginning_Date = new ProcedureParamItem("Beginning_Date");
            public static ProcedureParamItem Ending_Date = new ProcedureParamItem("Ending_Date");
        }
    }
}
